#include <regex.h>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <iostream>
#include <memory.h>
#include <fstream>
#include <vector>
#include <sstream>
#include "argparse.hpp"

typedef unsigned long long u64;
using namespace std;
// 一个视频最多划分的tile数量
#define MAX_TILE_NUMBER 30

// 每个tile都是一个vector，不再单独申请内存。
vector<unsigned long long> address[MAX_TILE_NUMBER];
// 读入和写入的文件
ifstream fin;
ofstream fout;
// 读取输入参数
ArgumentParser parser;
// 模拟的并行的线程数量
int parallel_threads_num = 0;
//一行最多有多少个tile，实际上一个视频是固定的tile划分，注意是从0开始的，而取的max是实际的tile index，所以max_tile_num_record上是有数据的
int max_tile_num_record;

// 判断子串是不是数字
inline bool str_is_digit(char *astr) {
    size_t alen = strlen(astr);
    for (int i = 0; i < alen; ++i) {
        if (!isdigit(astr[i]))return false;
    }
    return true;
}

// 判断子串是不是数字和字母组成
inline bool str_is_alnum(char *astr) {
    size_t alen = strlen(astr);
    for (int i = 0; i < alen; ++i) {
        if (!isalnum(astr[i]))return false;
    }
    return true;
}

inline bool str_is_tile(char *astr) {
    return astr[1] == 'i' && astr[2] == 'l' && astr[3] == 'e';
}

/** 用来模拟并行，输出混淆数据*/
void shuffle() {
    int maxn = 0;
    // circle: 每次处理的个数，主要是最后可能会小于线程 mymap:在这个cricle中，映射真正的index在address。
    int mymap[10], circle = 0;
    u64 total_num;
    int cur_threads = 0;
    u64 item_lens[10], item_index[10];
    for (; maxn <= max_tile_num_record; maxn += cur_threads + 1) {
        // 每次处理的是maxn, maxn+1,,,maxn+cur_threads tile的数据
        // maxn + cur_threads curthreads位置上有数据
        total_num = 0;

        if (maxn + parallel_threads_num - 1 < max_tile_num_record) {
            cur_threads = parallel_threads_num - 1;
        } else {
            cur_threads = max_tile_num_record % parallel_threads_num;
        }
        // 用于如果某个address空了以后，映射
        for (int i = 0; i <= cur_threads; ++i) {
            mymap[i] = maxn + i;
            item_lens[i] = address[maxn + i].size();
            total_num += item_lens[i];
            item_index[i] = 0;
        }
        stringstream strm;
        circle = cur_threads + 1;
        while (total_num > 0) {
            int rand_index = rand() % circle;
            // 把地址再写回文件
            strm.str("");
            strm.clear();
            // 取出在当前circle里rand_index的tile的记录的下一个
            strm << hex << address[mymap[rand_index]][item_index[rand_index]++];
            string temps = strm.str();
            if (temps[0] == '7')
                fout << 'l' << ' ' << temps.c_str();
            else
                //由于地址格式是a7f.....第一个字母是访问格式
                fout << temps.substr(0, 1) << " " << temps.substr(1);
            fout << endl;
            total_num--;
            // 如果某个list空了，就踢出去。
            if (item_index[rand_index] >= item_lens[rand_index]) {
                for (int i = rand_index; i < circle - 1; ++i) {
                    mymap[i] = mymap[i + 1];
                    item_index[i] = item_index[i + 1];
                    item_lens[i] = item_lens[i + 1];
                }
                circle--;
            }
        }
    }
}

void work(const char *input_file) {
    char output_file[100];
    strcpy(output_file, input_file);
    strcat(output_file, ".shuffle");
    fin.open(input_file);
    fout.open(output_file);
    char raw_line[38064];

    // 前63行是废弃的信息
    int i = 0;
    while (i < 63) {
        fin.getline(raw_line, 3806488);
        i++;
    }
    //一个一个字符的读取
    char inputc;
    //保存的当前读取完成的一个地址字符串
    char a_item[100];
    // 当前处理的地址属于哪个tile
    int cur_tile;
    // 转换hex字符串到unsigned long long
    stringstream strm;
    unsigned long long a_address;
    // 最多有多少个tile。
    max_tile_num_record = 0;
    // exit mark
    bool b_exit = false;
    while ((inputc = (char) fin.get()) != EOF && !b_exit) {
        int j;
        // 一行结束了
        if (inputc == '\n') {
            if (parallel_threads_num > 0) {
                shuffle();
            } else {
                printf("wrong config of parallel thread");
                return;
            }
            //最后清空
            for (int i = 0; i < MAX_TILE_NUMBER; ++i) {
                address[i].clear();
            }
        } else {
            // 继续读入数据
            switch (inputc) {
                // 如果是t,就读到空格为止，处理出当前正在处理的是哪个tile
                case 't':
                    j = 0;
                    a_item[j++] = 't';
                    // t开头的最后应该也不是\n
                    while ((a_item[j++] = (char) fin.get()) != ' ') {}
                    a_item[--j] = '\0';
                    //小于的话肯定不是tile序号
                    if (j >= 5 && str_is_tile(a_item) && str_is_digit(&a_item[4])) {
                        //保存当前的tile序号
                        cur_tile = atoi(&a_item[4]);
                        max_tile_num_record = max(max_tile_num_record, cur_tile);
                    } else {
                        //todo
                    }
                    break;
                    // 如果是正常的要读取的地址
                case 'a': //L1 lock
                case 'b':// L2 lock
                case 'c':// l1 unlock
                case 'd'://l2 unlock
                case '7': // normal read
                case 'S':
                    j = 0;
                    a_item[j++] = inputc;
                    while ((a_item[j++] = (char) fin.get()) != ' ') {}
                    a_item[--j] = '\0';
                    // 说明是一个地址
                    if (j >= 12 && str_is_alnum(a_item)) {
                        strm.str("");
                        strm.clear();
                        strm << hex << a_item;
                        strm >> a_address;
                        address[cur_tile].push_back(a_address);
                    } else {
                        if (strcmp("SUMMARY", a_item) == 0) {
                            b_exit = true;
                        }
                    }
                    break;
            }
        }
    }

    fin.close();
    fout.close();
}

int main(const int argc, const char *argv[]) {
    srand(time(NULL));
    parser.addArgument("-i", "--input", 1, false);
    parser.addArgument("-p", "--parallel", 1, true);
    parser.parse(argc, argv);
    printf("%s", parser.retrieve<string>("i").c_str());
    // 读入模拟的并行的线程，default 4
    // bug： 如果没有指定输入，就会出错。
//    parallel_threads_num = parser.retrieve<int>("p");
    if (parallel_threads_num < 1)
        parallel_threads_num = 4;
    // 注意返回值是string类，转化成char[]需要改东西
    work(parser.retrieve<string>("input").c_str());
    return 0;
}
